<?php

/**
 * This file is part of the Framework03 PHP framework.
 * @author Walter Tamboer
 * @package Framework03
 */

/**
 * The FW_Form class is able to create a form used on HTML webpages.
 */
class FW_Form extends FW_Html_Element
{
    /** The HTTP GET method. */
    const ACTION_GET = 'get';

    /** The HTTP POST method. */
    const ACTION_POST = 'post';

    /**
     * @var array A list with all the elements that this form contains.
     */
    private $elementList = array();

    /**
     * Initializes a new instance of this class.
     * @param string $action The action of this form.
     * @param string $method The method of this form.
     * @param array $options Possible options used to create the form.
     */
    public function __construct($action = '', $method = self::ACTION_POST, $options = null)
    {
        $this->setAttribute('action', $action);
        $this->setAttribute('method', $method);
		
		$this->initialize();
    }
	
	/**
	 * Called when this form should be initialized.
	 * @return bool Returns true when initializing succeeded; false otherwise.
	 */
	protected function initialize()
	{
		return true;
	}
    
    /**
     * Checks if there weren't generated any errors.
     * @param array $data The data to check against.
     * @return bool Returns true when there are no errors; false otherwise.
     */
    public function isValid(array $data = array())
    {
        if (count($data) < 1)
        {
            if ($this->getAttribute('method') == self::ACTION_POST)
                $data = $_POST;
            else
                $data = $_GET;
        }
		
        foreach ($this->elementList as $element)
        {
            $name = $element->getName();
            
            $value = isset($data[$name]) ? $data[$name] : null;

            if (!$element->isValid($value))
                return false;
        }

        return true;
    }

    /**
     * Checks if the request method is POST at the moment.
     * @return bool Returns true when the method is POST.
     */
    public function isPosted()
    {
        return ($_SERVER[ 'REQUEST_METHOD' ] == 'POST');
    }

    /**
     * Adds the given element to this form.
     * @param FW_Form_Element $element The element that should be added to this form.
     */
    public function addElement($element)
    {
        $this->elementList[] = $element;
    }
	
	/**
	 * Gets the element with the given name.
	 * @param string $name The name of the element to get.
	 * @return mixed Returns null if the element wasn't found; FW_Form_Element otherwise.
	 */
	public function getElement($name)
	{
        foreach ($this->elementList as $element)
        {
			if ($element->getName() == $name)
				return $element;
		}
		
		return null;
	}

    /**
     * Creates a new element of the given type.
     * @param string $type The type of the element to create.
     * @param string $name The name to give to the created element.
     * @param mixed $options Optional options used to create the element.
     * @return Returns the created element.
     * @throws FW_Form_Exception
     */
    public static function createElement($type, $name, $options = null)
    {
        if (!is_string($type))
            throw new FW_Form_Exception('Element type must be a string.');

        if (!is_string($name))
            throw new FW_Form_Exception('Element name must be a string.');

        $element = null;

        switch ($type)
        {
        case 'text':
            $element = new FW_Form_Element_Text($name, $options);
            break;

        case 'password':
            $element = new FW_Form_Element_Password($name, $options);
            break;

        case 'submit':
            $element = new FW_Form_Element_Submit($name, $options);
            break;

        case 'reset':
            $element = new FW_Form_Element_Reset($name, $options);
            break;

        case 'button':
            $element = new FW_Form_Element_Button($name, $options);
            break;

        default:
            throw new FW_Form_Exception('Can\'t create unknown form element of type "'.$type.'"');
        }

        return $element;
    }
    
    /**
     * Generates the code of which this form exists of.
     * @return string Returns the generated form code.
     */
    public function render()
    {
        $formCode = '';

        $formCode .= '<form' . $this->renderAttributes() . '>';
        foreach ($this->elementList as $element)
        {
            $formCode .= $element->render();
        }
        $formCode .= '</form>';

        return $formCode;
    }

    /**
     * Converts this form to a string and returns it.
     * @return string Returns the genereated form code.
     */
    public function __toString()
    {
        return $this->render();
    }
}
